#ifndef COMPILER_JS
#define COMPILER_JS

#include "Files.js"
#include "Parser/Project.js"
#include "ConceptCollection.js"
#include "Class.js"

var Compiler = {};

(function()
{
	/* void */ var FillFiles = function(/* Files */ files, /* Directory | Project */ directory, /* string */ parentPath)
	{
		var path = require("path");
	
		var currentPath = parentPath;
		if (directory.Name != undefined)
		{
			currentPath = path.join(currentPath, directory.Name);
		}
		
		var directories = directory.Directories;
		var directoryCount = directories.length;
		
		for (var i = 0; i < directoryCount; i++)
		{
			FillFiles(files, directories[i], currentPath);
		}
		
		var fileCount = directory.Files.length;
		for (var i = 0; i < fileCount; i++)
		{
			var file = directory.Files[i];
			
			if (file.Action != Action.None)
			{
				var sourcePath = path.join(currentPath, file.Name);
				
				if (file.Action == Action.Compile)
				{
					files.ToCompile.push(sourcePath);
				}
				else if (file.Action == Action.Copy)
				{
					files.ToCopy.push(sourcePath);
				}
			}
		}
	};
	
	/* Files */ var GenerateFiles = function(/* Directory | Project */ directory)
	{
		var files = new Files();
		
		FillFiles(files, directory, ".");
		
		return files;
	};
	
	/* void */ Compiler.Compile = function(/* string */ projectFileName)
	{
		var fs = require("fs");
		var path = require("path");
		
		var project = Project.Load(projectFileName);
		var files = GenerateFiles(project);
		
		var projectDirectoryPath = path.join(process.cwd(), path.dirname(projectFileName));
		
		var clean = function(line)
		{
			var reg=/[ \t]*$/g;
			line = line.replace(reg,"");
			
			var reg=/^[ \t]*/g;
			line = line.replace(reg,"");
			
			var reg=/[ ]*[(][ ]*/g;
			line = line.replace(reg,"(");
			
			var reg=/[ ]*[{][ ]*/g;
			line = line.replace(reg,"{");
			
			var reg=/[ ]*[)][ ]*/g;
			line = line.replace(reg,")");
			
			var reg=/[ ]*[}][ ]*/g;
			line = line.replace(reg,"}");
			
			var reg=/[ ]*[,][ ]*/g;
			line = line.replace(reg,",");
			
			var reg=/[ ]*[;][ ]*/g;
			line = line.replace(reg,";");
			
			var reg=/^(\/\/).*/g;
			line = line.replace(reg,"");
			
			return line;
		};
		
		var classes = new Array();
		var compiledCode = "";
		for (var i = 0; i < files.ToCompile.length; i++)
		{
			var sourcePath = path.join(projectDirectoryPath, files.ToCompile[i]);
			
			var sourceCode = fs.readFileSync(sourcePath, "utf-8");
			
			var cleanSourceCode = '';
			
			var multiLinesComment = false;
			sourceCode.split('\n').forEach(function(line) {
				if(line != '' && line != ' ' && line != '\t')
				{
					var cleanLine = clean(line);
					
					if(! multiLinesComment)
					{
						var reg=/(\/\*)/;
						if(reg.exec(cleanLine))
						{
							var cleanLineWithoutComment = "";
							
							var index = 0;
							var slashIdentified = false;
							var commentBegun = false;
							var starIdentified = false;
							
							while(index < cleanLine.length)
							{
								var currentChar = cleanLine[index];
								
								if(currentChar == '/')
								{
									if(slashIdentified && commentBegun && starIdentified)
									{
										slashIdentified = false;
										commentBegun = false;
										starIdentified = false;
									}
									else
									{
										slashIdentified = true;
									}
								}
								else
								{
									if(currentChar == '*')
									{
										if(commentBegun)
										{
											starIdentified = true;
										}
										else
										{
											if(slashIdentified)
											{
												commentBegun = true;
											} else {
												cleanLineWithoutComment += currentChar;
											}
										}
									}
									else
									{
										if(! commentBegun)
										{
											slashIdentified = false;
											cleanLineWithoutComment += currentChar;
										}
									}
								}
								
								index++;
							}
							
							if(commentBegun)
							{
								multiLinesComment = true;
							}
							else
							{
								if(slashIdentified) {
									cleanLineWithoutComment += '/';
								}
							}
							
							cleanSourceCode += cleanLineWithoutComment;
						}
						else
						{
							cleanSourceCode += cleanLine;
						}	
			    	}
			    	else
			    	{
			    		var reg=/(\*\/)/;
						if(reg.exec(cleanLine))
						{
							multiLinesComment = false;
						}
			    	}
			    }
			  });
			
			cleanSourceCode = clean(cleanSourceCode);
			
			var concepts = new ConceptCollection(cleanSourceCode);
			for (var j = 0; j < concepts.Length; j++)
			{
				var cl = new Class(concepts.At(j));
				
				compiledCode += cl.ToJs();
				
				classes.push(cl);
			}
		}
			
		for (var i = 0; i < classes.length; i++)
		{
			compiledCode += classes[i].ToStaticJs();
		}
		
		if (project.ExecutableEntryPoint != "")
		{
			compiledCode += project.ExecutableEntryPoint + ";";
		}
		
		var outputDirectoryPath = path.join(projectDirectoryPath, project.OutputDirectory);
		if (!path.existsSync(outputDirectoryPath))
		{
			fs.mkdirSync(outputDirectoryPath, 0755);
		}
		
		var outputFile = fs.openSync(path.join(outputDirectoryPath, project.OutputName), "w");
		fs.writeSync(outputFile, compiledCode);
		fs.closeSync(outputFile);
		
		for (var i = 0; i < files.ToCopy.length; i++)
		{
			// Create source and destination paths.
			var sourcePath = path.join(projectDirectoryPath, files.ToCopy[i]);
			var destinationPath = path.join(outputDirectoryPath, files.ToCopy[i]);
			
			// Read content from source.
			var content = fs.readFileSync(sourcePath);
			
			// Check destination directory existence.
			var destinationDirectoryPath = path.dirname(destinationPath);
			if (!path.existsSync(destinationDirectoryPath))
			{
				fs.mkdirSync(destinationDirectoryPath, 0755);
			}
			
			// Save the content to the new file.
			fs.writeFileSync(destinationPath, content);
		}
	};
})();

#endif
